﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using WeifenLuo.WinFormsUI.Docking;
using Pasys.PresentationCore.DockShell;
using Pasys.PresentationCore.DockShell.SmartPartInfos;
using System.Windows.Forms;
using System.Collections.ObjectModel;
using System.Diagnostics;
using Pasys.PresentationCore;
using System.IO;

namespace WinFormShell
{
    public class DockPanelWorkSpace : DockPanel, IComposableWorkspace<Control, DockPanelSmartPartInfo>,
                                      IDockPanelWorkspace
    {
        private readonly IWorkspaceComposer<Control> composer;
        private readonly Dictionary<Control, IDockContent> dockContents;
        private DeserializeDockContent m_deserializeDockContent;
        private bool isDisposing;


                /// <summary>
        /// Initializes a new instance of the <see cref="DockPanelWorkspace"/> class
        /// </summary>
        public DockPanelWorkSpace()
        {
            dockContents = new Dictionary<Control, IDockContent>();
            composer = CreateWorkspaceComposer();
            m_deserializeDockContent = new DeserializeDockContent(GetContentFromPersistString);
            ActiveContentChanged += ThisActiveContentChanged;
        }

        #region IWorkspace Members
        public virtual void LoadLayout(string fileName)
        {
            fileName = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "DockPanel.config");
            if (!File.Exists(fileName))
            {
                return;
            }
            this.SuspendLayout(true);

            // In order to load layout from XML, we need to close all the DockContents
            CloseAllDocuments();

            this.LoadFromXml(fileName, m_deserializeDockContent);
            this.ResumeLayout(true, true);

        }
        public virtual void SaveLayout(string fileName)
        {
            fileName = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "DockPanel.config");
            this.SaveAsXml(fileName);
        }

        private void CloseAllDocuments()
        {
            for (int i = this.DockWindows.Count - 1; i >= 0; i--)
            {
                var dw = this.DockWindows[i];
                //var c = dw.Controls[0];                
            }

            foreach (IDockContent document in this.DocumentsToArray())
            {
                document.DockHandler.Close();
            }

            for (int i = this.Contents.Count - 1; i >= 0; i--)  // [0].DockHandler.Close();
            {
                //if (!this.Contents[i].DockHandler.HideOnClose)
                //{
                //    this.Contents[i].DockHandler.Close();
                //}
                this.Contents[i].DockHandler.Close();
            }

        }

        #endregion

        #region IComposableWorkspace<Control,DockPanelSmartPartInfo> Members

        void IComposableWorkspace<Control, DockPanelSmartPartInfo>.OnActivate(Control smartPart)
        {
            OnActivate(smartPart);
        }

        void IComposableWorkspace<Control, DockPanelSmartPartInfo>.OnApplySmartPartInfo(Control smartPart,
                                                                                        DockPanelSmartPartInfo
                                                                                            smartPartInfo)
        {
            OnApplySmartPartInfo(smartPart, smartPartInfo);
        }

        void IComposableWorkspace<Control, DockPanelSmartPartInfo>.OnShow(Control smartPart,
                                                                          DockPanelSmartPartInfo smartPartInfo)
        {
            OnShow(smartPart, smartPartInfo);
        }

        void IComposableWorkspace<Control, DockPanelSmartPartInfo>.OnHide(Control smartPart)
        {
            OnHide(smartPart);
        }

        void IComposableWorkspace<Control, DockPanelSmartPartInfo>.OnClose(Control smartPart)
        {
            OnClose(smartPart);
        }

        void IComposableWorkspace<Control, DockPanelSmartPartInfo>.RaiseSmartPartActivated(WorkspaceEventArgs e)
        {
            OnSmartPartActivated(e);
        }

        void IComposableWorkspace<Control, DockPanelSmartPartInfo>.RaiseSmartPartClosing(WorkspaceCancelEventArgs e)
        {
            OnSmartPartClosing(e);
        }

        DockPanelSmartPartInfo IComposableWorkspace<Control, DockPanelSmartPartInfo>.ConvertFrom(ISmartPartInfo source)
        {
            return OnConvertFrom(source);
        }

        public event EventHandler<WorkspaceCancelEventArgs> SmartPartClosing;

        public event EventHandler<WorkspaceEventArgs> SmartPartActivated;

        public ReadOnlyCollection<object> SmartParts
        {
            get { return composer.SmartParts; }
        }

        public object ActiveSmartPart
        {
            get { return composer.ActiveSmartPart; }
        }

        public void Activate(object smartPart)
        {
            composer.Activate(smartPart);
        }

        public void ApplySmartPartInfo(object smartPart, ISmartPartInfo smartPartInfo)
        {
            composer.ApplySmartPartInfo(smartPart, smartPartInfo);
        }

        public void Close(object smartPart)
        {
            composer.Close(smartPart);
        }

        public void Hide(object smartPart)
        {
            composer.Hide(smartPart);
        }

        public void Show(object smartPart, ISmartPartInfo smartPartInfo)
        {
            composer.Show(smartPart, smartPartInfo);
        }

        public void Load(object smartPart)
        {
            composer.Load(smartPart);
        }


        public void Show(object smartPart)
        {
            composer.Show(smartPart);
        }

        #endregion

        #region IDockPanelWorkspace Members

        DockPanel IDockPanelWorkspace.DockPanel
        {
            get { return this; }
        }

        IDockContent IDockPanelWorkspace.GetDockContent(Control smartPart)
        {
            return dockContents[smartPart];
        }

        #endregion

        /// <summary>
        /// Create a WorkspaceComposer. GoF Design Pattern: Factory Method.
        /// </summary>
        protected virtual IWorkspaceComposer<Control> CreateWorkspaceComposer()
        {
            return new WorkspaceComposerAdapter<Control, DockPanelSmartPartInfo>(this);
        }

        /// <summary>
        /// Overriden to control when the workspace is being disposed to disable the control activation logic.
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            isDisposing = disposing;
            ActiveContentChanged -= ThisActiveContentChanged;
            base.Dispose(disposing);
        }

        #region Private

        private void ThisActiveContentChanged(object sender, EventArgs e)
        {
            if (ActiveContent == null)
            {
                return;
            }

            Control smartPart = GetSmartPart(ActiveContent);

            if (smartPart != null)
            {
                Activate(smartPart);
            }
            else
            {
                composer.SetActiveSmartPart(null);
            }
        }

        // Raise the SmartPartClosing event and use the returned value.
        private void FormClosing(object sender, FormClosingEventArgs e)
        {
            Control smartPart = GetSmartPart((IDockContent) sender);
            if (smartPart != null)
            {
                var wce = new WorkspaceCancelEventArgs(smartPart);
                OnSmartPartClosing(wce);
                e.Cancel = wce.Cancel;
            }
        }

        private void ControlDisposed(object sender, EventArgs e)
        {
            var control = sender as Control;
            if (!isDisposing && control != null)
            {
                composer.ForceClose(control);
            }
        }

        private Control GetSmartPart(IDockContent dockContent)
        {
            // Locate the smart part corresponding to the dockContent.
            foreach (var pair in dockContents)
            {
                if (pair.Value == dockContent)
                {
                    return pair.Key;
                }
            }

            // not found
            return null;
        }

        #endregion

        #region Protected virtual implementation

        /// <summary>
        /// Create an IDockContent object that hosts the smart part.
        /// </summary>
        protected virtual IDockContent CreateDockContent(Control smartPart, DockPanelSmartPartInfo smartPartInfo)
        {
            var f = smartPart as Form;
            if (f != null)
            {
                f.TopLevel = false;
                f.FormBorderStyle = FormBorderStyle.None;
                f.Visible = true;
            }


            var dockContent = new DockContent();
            dockContent.Name = Guid.NewGuid().ToString();
            dockContent.BackColor = ProfessionalColors.MenuBorder;
            dockContent.Padding = new Padding(1);
            dockContent.HideOnClose = smartPartInfo.HideOnClose;
            var backgroundPanel = new Panel();
            backgroundPanel.BackColor = ProfessionalColors.ToolStripGradientMiddle;
            backgroundPanel.Dock = DockStyle.Fill;

            smartPart.Dock = DockStyle.Fill;

            dockContent.Controls.Add(backgroundPanel);
            backgroundPanel.Controls.Add(smartPart);
            if (f != null)
            {
                dockContent.Text = f.Text;
                f.TextChanged += (s, e) => {
                    dockContent.Text = f.Text;
                };

                //dockContent.FormClosing += (s, e) =>
                //{
                //    if (f != null)
                //    {
                //        f.Close();
                //    }
                //};
            }
            var callback = smartPartInfo.PersistStringCallback;
            dockContent.DockHandler.GetPersistStringCallback = () => { return callback; };
            return dockContent;
        }

        /// <summary>
        /// Converts a smart part information to a compatible one for the workspace.
        /// </summary>
        protected virtual DockPanelSmartPartInfo OnConvertFrom(ISmartPartInfo source)
        {
            return DockPanelSmartPartInfo.ConvertTo(source);
        }

        /// <summary>
        /// Activate the smart part.
        /// </summary>
        protected virtual void OnActivate(Control smartPart)
        {
            //this.ActiveAutoHideContent = dockContents[smartPart];
            if (dockContents[smartPart].DockHandler.IsActivated)
            {
                return;
            }

            switch (dockContents[smartPart].DockHandler.DockState)
            {
                case WeifenLuo.WinFormsUI.Docking.DockState.DockBottomAutoHide:
                    dockContents[smartPart].DockHandler.DockState = WeifenLuo.WinFormsUI.Docking.DockState.DockBottom;
                    break;
                case WeifenLuo.WinFormsUI.Docking.DockState.DockLeftAutoHide:
                    dockContents[smartPart].DockHandler.DockState = WeifenLuo.WinFormsUI.Docking.DockState.DockLeft;
                    break;
                case WeifenLuo.WinFormsUI.Docking.DockState.DockRightAutoHide:
                    dockContents[smartPart].DockHandler.DockState = WeifenLuo.WinFormsUI.Docking.DockState.DockRight;
                    break;
                case WeifenLuo.WinFormsUI.Docking.DockState.DockTopAutoHide:
                    dockContents[smartPart].DockHandler.DockState = WeifenLuo.WinFormsUI.Docking.DockState.DockTop;
                    break;
                default:
                    break;
            }
            dockContents[smartPart].DockHandler.Activate();
        }

        /// <summary>
        /// Apply the DockPanelSmartPartInfo at the IDockContent container.
        /// </summary>
        protected virtual void OnApplySmartPartInfo(Control smartPart, DockPanelSmartPartInfo smartPartInfo)
        {
            IDockContent dockContent = dockContents[smartPart];
            dockContent.DockHandler.TabText = smartPartInfo.Title;
            dockContent.DockHandler.ToolTipText = smartPartInfo.Description;
            dockContent.DockHandler.Form.Icon = smartPartInfo.Icon;
            switch (smartPartInfo.DockingType)
            {
                case DockingType.Document:
                    dockContent.DockHandler.DockAreas = DockAreas.Document;
                    break;
                case DockingType.TaskView:
                    dockContent.DockHandler.DockAreas = DockAreas.DockBottom | DockAreas.DockLeft | DockAreas.DockRight |
                                                        DockAreas.DockTop | DockAreas.Float;
                    break;
                default:
                    dockContent.DockHandler.DockAreas = DockAreas.Document|
                                                        DockAreas.DockBottom | DockAreas.DockLeft | DockAreas.DockRight |
                                                        DockAreas.DockTop | DockAreas.Float;
                    break;
            }

            //var ps = dockContent.DockHandler.GetType().GetProperties();
            //foreach (var p in ps)
            //{
            //    Console.WriteLine("{0} => {1}", p.Name, p.GetValue(dockContent.DockHandler, null));
            //}
        }

        /// <summary>
        /// Close the window that contains the smart part.
        /// </summary>
        protected virtual void OnClose(Control smartPart)
        {
            // Prevent the closing of the same smartPart during DockHandler.Close()
            if (dockContents.ContainsKey(smartPart))
            {
                IDockContent dockContent = dockContents[smartPart];
                dockContents.Remove(smartPart);

                dockContent.DockHandler.Close();
                dockContent.DockHandler.Form.FormClosing -= FormClosing;
                smartPart.Disposed -= ControlDisposed;
            }
        }

        /// <summary>
        /// Hide the window that contains the smart part.
        /// </summary>
        protected virtual void OnHide(Control smartPart)
        {
            dockContents[smartPart].DockHandler.Hide();
        }

        /// <summary>
        /// Show the SmartPart inside a DockContent container and use the DockPanelSmartPartInfo to
        /// control the appearance.
        /// </summary>
        protected virtual void OnShow(Control smartPart, DockPanelSmartPartInfo smartPartInfo)
        {
            IDockContent dockContent;
            if (dockContents.ContainsKey(smartPart))
            {
                dockContent = dockContents[smartPart];
            }
            else
            {
                dockContent = smartPart as IDockContent;
                if (dockContent == null)
                {
                    dockContent = CreateDockContent(smartPart, smartPartInfo);
                }
               
                dockContents.Add(smartPart, dockContent);
                smartPart.Disposed += ControlDisposed;
                dockContent.DockHandler.Form.FormClosing += FormClosing;
                OnApplySmartPartInfo(smartPart, smartPartInfo);
            }
            smartPartInfo.ShowStrategy.Show(this, dockContent);
        }

        /// <summary>
        /// Raises the <see cref="SmartPartActivated"/> event.
        /// </summary>
        protected virtual void OnSmartPartActivated(WorkspaceEventArgs e)
        {
            if (SmartPartActivated != null)
            {
                SmartPartActivated(this, e);
            }
        }

        /// <summary>
        /// Raises the <see cref="SmartPartClosing"/> event.
        /// </summary>
        protected virtual void OnSmartPartClosing(WorkspaceCancelEventArgs e)
        {
            if (SmartPartClosing != null)
            {
                SmartPartClosing(this, e);
            }
        }

        #endregion


        private IDockContent GetContentFromPersistString(string persistString)
        {
            var provider = AbstractViewProvider.FromPersistStringCallback(persistString);
            if (provider == null)
            {
                return null;
            }

            provider.SetPersistStringCallbackParam(persistString);
            var smartPartKey = provider.View;
            if (smartPartKey == null)
            {
                return null;
            }

            var smartPart = smartPartKey;
            int i = SmartParts.IndexOf(smartPartKey);
            if (i >= 0)
            {
                smartPart =(Control) SmartParts[i];
                if (smartPart != smartPartKey)
                {
                    var d = smartPartKey as IDisposable;
                    if (d != null)
                    {
                        d.Dispose();
                    }
                    smartPartKey = null;
                }

                Activate(smartPart);
                return null;
            }

            if (!SmartParts.Contains(smartPart))
            {
                composer.Load(smartPart);
            }


            var smartPartInfo = provider.ViewInfo as DockPanelSmartPartInfo;

            if (smartPartInfo == null)
            {
                smartPartInfo = new DockPanelSmartPartInfo()
                {
                    DockingType = DockingType.Document,
                    ShowStrategy = new DockStateShowStrategy(Pasys.PresentationCore.DockShell.SmartPartInfos.DockState.Document),
                };
            }

            smartPartInfo.PersistStringCallback = persistString;
            var f = smartPart as Form;
            var info = smartPartInfo as ISmartPartInfo;
            if (f != null && info != null)
            {
                if (string.IsNullOrEmpty(info.Title))
                {
                    info.Title = f.Text;
                }

                if (string.IsNullOrEmpty(info.Description))
                {
                    info.Description = f.Text;
                }
            }

            var iconInfo = smartPartInfo as IconSmartPartInfo;
            if (f != null && iconInfo != null)
            {
                if (iconInfo.Icon == null && f.Icon != null)
                {
                    iconInfo.Icon = f.Icon;
                }
            }


            IDockContent dockContent = smartPart as IDockContent;
            if (dockContent == null)
            {
                dockContent = CreateDockContent(smartPart, smartPartInfo);
            }

            dockContents.Add(smartPart, dockContent);
            smartPart.Disposed += ControlDisposed;
            dockContent.DockHandler.Form.FormClosing += FormClosing;
            OnApplySmartPartInfo(smartPart, smartPartInfo);

            return dockContent;

        }

    }
}
